﻿using System;
using System.Collections.Generic;
using System.Linq;
using Cubicle.Core.Config;
using Cubicle.Core.DataContracts;
using Cubicle.Core.Interfaces;
using Cubicle.Core.Processors;
using Cubicle.Core.Shared;
using Cubicle.Excel.Config;
using Cubicle.Excel.Functions;
using Cubicle.Excel.Interfaces;
using Cubicle.Excel.Shared;
using ExcelDna.Integration;

namespace Cubicle.Excel.Hosting
{
    public class LocalProxy : EngineProxy
    {
        // Nlog
        private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();

        public LocalProxy(string alias, Processor<object> marshalQueue, IEngine proxy)
            : base(alias, marshalQueue)
        {
            Proxy = proxy;
        }

        public IEngine Proxy { get; protected set; }
        public override string Address { get { return "Local"; } }
        public override bool IsConnected() { return true; }

        public override string StartHost(HostingType type, bool enableMetaData, int port)
        {
            return Proxy.StartHost(type, enableMetaData, port);
        }

        public override bool StopHost() { return Proxy.StopHost(); }
        public override bool IsHosted { get { return Proxy.IsHosted(); } }
        public override string HostAddress { get { return Proxy.GetHostAddress(); } }

        public override object ProxyInvoke(
            string handle,
            string memberName,
            object[,] parameters,
            object objKey,
            object[] methodArgs = null,
            object[] classArgs = null)
        {
            var key = objKey == ExcelMissing.Value || objKey == null  ? null : objKey.ToString();
            var inst = CustomHelpers.CreateInstruction(handle, memberName, parameters, key, methodArgs, classArgs);
            return Proxy.Process(inst);
        }

        #region Implementation of IRepository

        public override void Clear()
        {
            Proxy.Clear();
        }

        public override string[] List()
        {
            return Proxy.List();
        }

        public override string Rename(string handle, string newKey)
        {
            return Proxy.Rename(handle, newKey);
        }

        public override IRObject Remove(string handle)
        {
            return Proxy.Remove(handle);
        }

        public override bool Has(string handle)
        {
            return Proxy.Has(handle);
        }

        public override string Set(string key, object item)
        {
            return Proxy.Set(key, item);
        }

        public override object GetProxy(string handle)
        {
            return Proxy.Get(handle);
        }

        public override IRObject GetRObject(string handle)
        {
            return Proxy.GetRObject(handle);
        }

        #endregion

        #region Implementation of IEngine

        public override string Id
        {
            get
            {
                return Proxy.GetId();
            }
        }

        public override object Process(Instruction instruction)
        {
            object result = null;
            result = Proxy.Process(instruction);
            return result == null
                       ? result
                       : ExcelHelpers.JaggedToDouble(result);
        }

        public override bool Build(ProcessorConfig config)
        {
            Proxy.Build(config);
            return true;
        }

        #endregion

        #region IUserInterface

        public override string GetVersion()
        {
            return Proxy.GetVersion();
        }

        public override IEnumerable<AssemblyData> GetAssemblyDetails()
        {
            return Proxy.GetAssemblyDetails();
        }

        public override IEnumerable<MemberData> GetMembers(string target)
        {
            return Proxy.GetMembers(target);
        }

        public override IEnumerable<TypeData> GetTypes(string target)
        {
            return Proxy.GetTypes(target);
        }

        public override TypeData GetType(string target)
        {
            return Proxy.GetTypeData(target);
        }

        public override IEnumerable<MethodData> GetMethods(string target)
        {
            return Proxy.GetMethods(target);
        }

        public override IEnumerable<FieldData> GetFields(string target)
        {
            return Proxy.GetFields(target);
        }

        public override IEnumerable<PropertyData> GetProperties(string target)
        {
            return Proxy.GetProperties(target);
        }

        public override IEnumerable<ConstructorData> GetConstructors(string target)
        {
            return Proxy.GetConstructors(target);
        }

        public override IEnumerable<ParameterData> GetParameterData(string target, string memberName, int offset)
        {
            return Proxy.GetParameterData(target, memberName, offset);
        }

        #endregion

        #region Implementation of IInvocationEngine

        public override IEnumerable<string> GetLog(int i)
        {
            return Proxy.GetLog(i);
        }

        #endregion

        #region Implementation of IScriptManager

        public override bool RunScript(byte[] rawScript)
        {
            // First run normal script
            var result = false;
            result = Proxy.RunByteScript(rawScript);

            // If execution failed return
            if (!result) return false;

            // Only proceeed with binding if this is local and succeeded running
            var bindScript = Helpers.BinaryToXml<BindingScript>(rawScript);
            if (bindScript == null) return true;

            // Execute binding
            BindingFunctions.BindScript(bindScript, this);
            return result;
        }

        #endregion

        #region Assembly loading

        public override string LoadAssembly(byte[] assembly, byte[] symbols = null)
        {
            // For assemblies loaded locally, we must initialize ExcelAware instances
            var fullName = Proxy.LoadAssembly(assembly, symbols);
            InitExcelAware(fullName);
            return fullName;
        }

        /// <summary>
        /// Instantiate and run AutoOpen for all ExcelAware types
        /// </summary>
        /// <param name="fullName"></param>
        private static void InitExcelAware(string fullName)
        {
            var asm = AppDomain
                .CurrentDomain
                .GetAssemblies()
                .Where(a => a.FullName == fullName).FirstOrDefault();
            if (asm == null) return;

            var interfaceName = typeof(IExcelAware).Name;
            var excelAware = asm.GetTypes().Where(t => t.GetInterface(interfaceName) != null);
            try
            {
                foreach (var t in excelAware)
                {
                    var item = (IExcelAware)Activator.CreateInstance(t);
                    item.AutoOpen();
                }
            }
            catch (Exception e)
            {
                _logger.ErrorException(e, "Error initialising ExcelAware instance");
            }
        }

        #endregion

    }
}